
#include "../include/types.h"
#include "../include/const.h"
#include "../include/string.h"
#include "../include/monitor.h"
#include "../include/kernelheap.h"
#include "../include/process.h"
#include "../include/task.h"
#include "../include/console.h"

/* Eilës */
task_t *queue_1;				/* Kernel mode */
/*task_t *queue_2;*/				/* User mode */	/* TODO */

/* Procesai */
task_t *curr_task;			/* Einamasis procesas */
task_t *prev_task;			/* Ankstesnis procesas */

/* Proceso numeris */
int process_id;

/* Timer interrupt skaièius */
u32int ticks;

void init_multitasking() {
	curr_task = NULL;
	prev_task = NULL;
	process_id = 1;

	/* Sukuriamas ðakninis sistemos procesas */
	queue_1 = create_task(init, "init", KERNEL_MODE, 0);
}

task_t* create_task(void (*thread)(), char name[64], int priority, u32int console) {
	u32int *stack;
	if (priority == 1) {
		task_t *task = (task_t*) kmalloc(sizeof(task_t));	/* Iðskiriame atmintá uþduoties apraðui */
		task->esp0 = kmalloc_align(0x1000) + 0x1000;			/* Sukuriame stekà uþduoèiai */
		
		stack = (u32int*)task->esp0;											/* Sukuriame nuorodà á stekà */

		/* Á stekà ákrauname pradinæ CPU bûsenà */
		*--stack = 0x202; /* EFLAGS */
		*--stack = 0x08; 	/* CS */
		*--stack = (u32int)thread; /* EIP */
		
		*--stack = 0; 		/* EDI */
		*--stack = 0; 		/* ESI */
		*--stack = 0; 		/* EBP */
		*--stack = 0; 		/* Offset */
		*--stack = 0; 		/* EBX */
		*--stack = 0; 		/* EDX */
		*--stack = 0; 		/* ECX */
		*--stack = 0; 		/* EAX */
		
		*--stack = 0x10; 	/* DS */
		*--stack = 0x10; 	/* ES */
		*--stack = 0x10; 	/* FS */
		*--stack = 0x10; 	/* GS */
		
		/* Uþpildome uþduoties apraðà */
		task->esp0 = (u32int)stack;		/* Kernel mode stekas */
		task->esp3 = 0;								/* User mode stekas */
		task->id = new_id();					/* Proceso ID */
		strcpy(task->name, name);			/* Proceso pavadinimas */
		task->state = READY;					/* Proceso bûsena */
    task->console = console;
		task->priority = priority;		/* Proceso prioritetas */

		task->childs = NULL;					/* Proceso vaikai */
		
		if (curr_task == NULL ) {			/* Jeigu pirmas sistemos procesas */
			task->father = NULL;				/* Tai tëvo neturi */
			
			task->next = task;					/* Ir nuorodos rodi á save */
			task->prev = task;
		} else {											/* Jei nebe pirmas sistemos procesas */
			task->father = curr_task;		/* Tai tëvas yra einamasis procesas */
			
			/* Ákeliame procesà á uþduoèiø eilæ */
			if (curr_task->prev == queue_1) {	/* Jei keliama á procesø eilës galà */
				task->next = curr_task;
				task->prev = queue_1;
				curr_task->prev = task;
				queue_1->next = task;
			} else {													/* Jei keliama á procesø eilës vidurá */
				task->next = curr_task;
				task->prev = curr_task->prev;
				curr_task->prev->next = task;
				curr_task->prev = task;
			}
		}
    if(strcmp("init", name)){
      cmd_find(console)->task = task;
    }
		return task;	/* Gràþiname sukurtà uþduotá */
	} else {
		/* TODO */
	}
}

u32int task_switch(u32int old_esp) {
	ticks++;
  task_t *zombie_task;
  task_t *tmp_task = curr_task->next;
  
	if (curr_task != NULL) {
    /* Pašaliname visus ZOMBIE procesus */
    do{
      if(tmp_task->state == ZOMBIE) {
        zombie_task = tmp_task;
        
        tmp_task->prev->next = tmp_task->next;
        tmp_task->next->prev = tmp_task->prev;
        
        tmp_task = tmp_task->next;
        
        if (zombie_task->priority == KERNEL_MODE) {
          kfree(zombie_task);
        } else {
          /* TODO */
        }
      }
      else{
        tmp_task = tmp_task->next;
      }
    }while(tmp_task != curr_task);
    
		if(curr_task->state == ZOMBIE) {
			zombie_task = curr_task;
			
			curr_task->prev->next = curr_task->next;
			curr_task->next->prev = curr_task->prev;
			
			curr_task = curr_task->next;
			prev_task = curr_task->prev;
			
			if (zombie_task->priority == KERNEL_MODE) {
				kfree(zombie_task);
			} else {
				/* TODO */
			}
		} 
    else {
			if (curr_task->priority == KERNEL_MODE) {
				curr_task->esp0 = old_esp;
			} else {
				curr_task->esp3 = old_esp;
			}
			
			prev_task = curr_task;

			curr_task = curr_task->next;
		}
	} else {
		curr_task = queue_1;
	}
	if (curr_task->priority == KERNEL_MODE) {
		return curr_task->esp0;
	} else {
		return curr_task->esp3;
	}
}

int new_id() {
	/* Naujas unikalus ID sistemoje */
	return process_id++;
}

task_t* current_task() {
	/* Vykdomoji uþduotis */
	return curr_task;
}

void add_child(task_t *child) {
	if (curr_task->childs == NULL) {				/* Jei dar procesas neturëjo vaikø */
		curr_task->childs = (child_t*) kmalloc(sizeof(child_t));	/* Iðskiriame vietos naujo vaiko áraðui */
		curr_task->childs->childr = child;		/* Nuoroda á vaikà */
		curr_task->childs->next = NULL;				/* Kito vaiko nëra */
	} else {																/* Jei procesas turëjo vaikø */
		child_t *tmp_child;
		tmp_child = (child_t*) kmalloc(sizeof(child_t));	/* Iðskiriame vietos naujo vaiko áraðui */
		tmp_child->childr = child;						/* Nuoroda á vaikà */
		tmp_child->next = curr_task->childs;	/* Nuoruoda á kità vaikà */
		curr_task->childs = tmp_child;				/* Nuoroda á vaikà */
	}
}

void suspend_task(task_t *task) {
  task->state = WAIT;
}

void resume_task(task_t *task) {
  task->state = READY;
}

int find_task(int id){
  task_t *tmp_task = curr_task;
  do{
    if(id == tmp_task->id){
      return TRUE;
    }
    tmp_task = tmp_task->next;
  }while(tmp_task != curr_task);
  return FALSE;
}

void finish_task() {
	if (curr_task->childs != NULL) {
		kill_childs(curr_task);			/* Iðjungiame visus vaikus */
	}
	if (curr_task->id != 1) {
		remove_father(curr_task);
	}
  finish(curr_task);
	curr_task->state = ZOMBIE;	/* Pakeièiam bûsenà */
	for (;;) ;									/* Laukiam task_switch */
}

void kill_childs(task_t *task) {
	child_t *tmp_child;
	while (task->childs->next != NULL) {
		kill(task->childs->childr->id);			/* Iðjungiame vaikà */
		tmp_child = task->childs;						/* Einamasis vaikas */ 
		task->childs = task->childs->next;
		kfree(tmp_child);							/* Atlaisviname vaiko apraðo naudojamà atmintá */
	}
	kill(task->childs->childr->id);	/* Iðjungiame vaikà */
	kfree(task->childs);						/* Atlaisviname vaiko apraðo naudojamà atmintá */
	task->childs = NULL;						/* Vaikø nebëra */
}

void remove_father (task_t *task) {
	boolean finished;
	child_t *father_childs;
	child_t *tmp_childs;
	finished = FALSE;
	father_childs = task->father->childs;
	tmp_childs = father_childs;
	
	if (father_childs->next != NULL) {
		while (!finished) {
			if (father_childs->childr->id == task->id) {
				if (father_childs == tmp_childs) {
					task->father->childs = task->father->childs->next;
				}	
        else {
					while (tmp_childs->next != father_childs) {
						tmp_childs = tmp_childs->next;
					}
					tmp_childs->next = father_childs->next;
				}
				kfree(father_childs);
				task->father = NULL;
				finished = TRUE;
			} else {
				father_childs = father_childs->next;
			}
		}
	} else {
		kfree(father_childs);
		task->father->childs = NULL;
		task->father = NULL;
	}
}

void kill (int id) {
	boolean finished;
	task_t *tmp_task;
	finished = FALSE;
	tmp_task = curr_task->next;
	
	while (!finished) {
		if (tmp_task->id == id) {		/* Jeigu radome ieðkomà uþduotá */
			if (tmp_task->childs != NULL) {
				kill_childs(tmp_task);		/* Iðjungiame vaikus */
			}
			if (tmp_task->id != 1) {	/* Jeigu ne REINC procesas */
				remove_father(tmp_task);
			}
			finish(tmp_task);
			tmp_task->state = ZOMBIE;
			finished = TRUE;
		} else {
			tmp_task = tmp_task->next;
		}
	}
}
